{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Welcome to Jupyter Notebook"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Contents\n",
    "### [Introduction to Jupyter Notebooks](https://www.youtube.com/watch?v=HW29067qVWk)\n",
    "### [Markdown Cheat Sheet](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet)\n",
    "### [Jupyter Notebook Short Cuts, Tips and Tricks](https://www.dataquest.io/blog/jupyter-notebook-tips-tricks-shortcuts/)\n",
    "### [Gallery of Jupyter Notebooks](http://nb.bianp.net/sort/views/)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Hello World (from Python)\n",
    "\n",
    "**Run this code below!**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hello World!\n"
     ]
    }
   ],
   "source": [
    "print(\"Hello World!\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Change your interface from classic Jupyter to Jupyter Lab by replacing **tree** with **lab** in the URL. Click on the link below. Jupyter Lab is an experimental feature of Jupyter Notebooks which introduces user interface improvements to the Notebook experience.\n",
    "\n",
    "> https://[site-name]/user/[username]/lab\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Hello World (from Geoviews)\n",
    "\n",
    "\"Hello World\" from Python is a little boring. Let's do a more visual \"Hello World\". This next \"Hello World\" exercise is based on the Geoviews module (https://www.continuum.io/blog/developer-blog/introducing-geoviews). Just hit \"play\" button on the following cells."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "<link rel=\"stylesheet\" href=\"https://code.jquery.com/ui/1.10.4/themes/smoothness/jquery-ui.css\">\n",
       "<style>div.bk-hbox {\n",
       "    display: flex;\n",
       "    justify-content: center;\n",
       "}\n",
       "\n",
       "div.bk-hbox div.bk-plot {\n",
       "    padding: 8px;\n",
       "}\n",
       "\n",
       "div.bk-hbox div.bk-data-table {\n",
       "    padding: 20px;\n",
       "}\n",
       "\n",
       "div.hololayout {\n",
       "  display: flex;\n",
       "  align-items: center;\n",
       "  margin: 0;\n",
       "}\n",
       "\n",
       "div.holoframe {\n",
       "  width: 75%;\n",
       "}\n",
       "\n",
       "div.holowell {\n",
       "  display: flex;\n",
       "  align-items: center;\n",
       "}\n",
       "\n",
       "form.holoform {\n",
       "  background-color: #fafafa;\n",
       "  border-radius: 5px;\n",
       "  overflow: hidden;\n",
       "  padding-left: 0.8em;\n",
       "  padding-right: 0.8em;\n",
       "  padding-top: 0.4em;\n",
       "  padding-bottom: 0.4em;\n",
       "  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);\n",
       "  margin-bottom: 20px;\n",
       "  border: 1px solid #e3e3e3;\n",
       "}\n",
       "\n",
       "div.holowidgets {\n",
       "  padding-right: 0;\n",
       "  width: 25%;\n",
       "}\n",
       "\n",
       "div.holoslider {\n",
       "  min-height: 0 !important;\n",
       "  height: 0.8em;\n",
       "  width: 100%;\n",
       "}\n",
       "\n",
       "div.holoformgroup {\n",
       "  padding-top: 0.5em;\n",
       "  margin-bottom: 0.5em;\n",
       "}\n",
       "\n",
       "div.hologroup {\n",
       "  padding-left: 0;\n",
       "  padding-right: 0.8em;\n",
       "  width: 100%;\n",
       "}\n",
       "\n",
       ".holoselect {\n",
       "  width: 92%;\n",
       "  margin-left: 0;\n",
       "  margin-right: 0;\n",
       "}\n",
       "\n",
       ".holotext {\n",
       "  padding-left:  0.5em;\n",
       "  padding-right: 0;\n",
       "  width: 100%;\n",
       "}\n",
       "\n",
       ".holowidgets .ui-resizable-se {\n",
       "  visibility: hidden\n",
       "}\n",
       "\n",
       ".holoframe > .ui-resizable-se {\n",
       "  visibility: hidden\n",
       "}\n",
       "\n",
       ".holowidgets .ui-resizable-s {\n",
       "  visibility: hidden\n",
       "}\n",
       "\n",
       "\n",
       "/* CSS rules for noUISlider based slider used by JupyterLab extension  */\n",
       "\n",
       ".noUi-handle {\n",
       "  width: 20px !important;\n",
       "  height: 20px !important;\n",
       "  left: -5px !important;\n",
       "  top: -5px !important;\n",
       "}\n",
       "\n",
       ".noUi-handle:before, .noUi-handle:after {\n",
       "  visibility: hidden;\n",
       "  height: 0px;\n",
       "}\n",
       "\n",
       ".noUi-target {\n",
       "  margin-left: 0.5em;\n",
       "  margin-right: 0.5em;\n",
       "}\n",
       "</style>\n",
       "\n",
       "\n",
       "<div class=\"logo-block\">\n",
       "<img src='\n",
       "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\n",
       "roTBs2RqAAAAAElFTkSuQmCC'\n",
       "     style='height:25px; border-radius:12px; display: inline-block; float: left; vertical-align: middle'></img>\n",
       "\n",
       "\n",
       "\n",
       "\n",
       "  <img src='\n",
       "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'\n",
       "       style='height:15px; border-radius:12px; display: inline-block; float: left'></img>\n",
       "  \n",
       "\n",
       "\n",
       "</div>\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/javascript": [
       "function HoloViewsWidget() {\n",
       "}\n",
       "\n",
       "HoloViewsWidget.prototype.init_slider = function(init_val){\n",
       "  if(this.load_json) {\n",
       "    this.from_json()\n",
       "  } else {\n",
       "    this.update_cache();\n",
       "  }\n",
       "}\n",
       "\n",
       "HoloViewsWidget.prototype.populate_cache = function(idx){\n",
       "  this.cache[idx].innerHTML = this.frames[idx];\n",
       "  if (this.embed) {\n",
       "    delete this.frames[idx];\n",
       "  }\n",
       "}\n",
       "\n",
       "HoloViewsWidget.prototype.process_error = function(msg){\n",
       "}\n",
       "\n",
       "HoloViewsWidget.prototype.from_json = function() {\n",
       "  var data_url = this.json_path + this.id + '.json';\n",
       "  $.getJSON(data_url, $.proxy(function(json_data) {\n",
       "    this.frames = json_data;\n",
       "    this.update_cache();\n",
       "    this.update(0);\n",
       "  }, this));\n",
       "}\n",
       "\n",
       "HoloViewsWidget.prototype.dynamic_update = function(current){\n",
       "  if (current === undefined) {\n",
       "    return\n",
       "  }\n",
       "  this.current = current;\n",
       "  if (this.comm) {\n",
       "    var msg = {comm_id: this.id+'_client', content: current}\n",
       "    this.comm.send(msg);\n",
       "  }\n",
       "}\n",
       "\n",
       "HoloViewsWidget.prototype.update_cache = function(force){\n",
       "  var frame_len = Object.keys(this.frames).length;\n",
       "  for (var i=0; i<frame_len; i++) {\n",
       "    if(!this.load_json || this.dynamic)  {\n",
       "      var frame = Object.keys(this.frames)[i];\n",
       "    } else {\n",
       "      var frame = i;\n",
       "    }\n",
       "    if(!(frame in this.cache) || force) {\n",
       "      if ((frame in this.cache) && force) { this.cache[frame].remove() }\n",
       "      var div = document.createElement(\"div\");\n",
       "      var parent = document.getElementById(\"_anim_img\"+this.id);\n",
       "      div.style.display = \"none\";\n",
       "      parent.appendChild(div)\n",
       "      this.cache[frame] = div;\n",
       "      var cache_id = \"_anim_img\"+this.id+\"_\"+frame;\n",
       "      this.populate_cache(frame);\n",
       "    }\n",
       "  }\n",
       "}\n",
       "\n",
       "HoloViewsWidget.prototype.update = function(current){\n",
       "  if(current in this.cache) {\n",
       "    for (var index in this.cache) {\n",
       "      this.cache[index].style.display = \"none\";\n",
       "    }\n",
       "    this.cache[current].style.display = \"\";\n",
       "    this.wait = false;\n",
       "  }\n",
       "}\n",
       "\n",
       "HoloViewsWidget.prototype.init_comms = function() {\n",
       "  var that = this\n",
       "  HoloViews.comm_manager.register_target(this.plot_id, this.id, function (msg) { that.process_msg(msg) })\n",
       "  if (!this.cached || this.dynamic) {\n",
       "    function ack_callback(msg) {\n",
       "      msg = JSON.parse(msg.content.data);\n",
       "      var comm_id = msg[\"comm_id\"]\n",
       "      var comm_status = HoloViews.comm_status[comm_id];\n",
       "      if (that.queue.length > 0) {\n",
       "        that.time = Date.now();\n",
       "        that.dynamic_update(that.queue[that.queue.length-1]);\n",
       "        that.queue = [];\n",
       "      } else {\n",
       "        that.wait = false;\n",
       "      }\n",
       "      if ((msg.msg_type == \"Ready\") && msg.content) {\n",
       "        console.log(\"Python callback returned following output:\", msg.content);\n",
       "      } else if (msg.msg_type == \"Error\") {\n",
       "        console.log(\"Python failed with the following traceback:\", msg['traceback'])\n",
       "      }\n",
       "    }\n",
       "    var comm = HoloViews.comm_manager.get_client_comm(this.plot_id, this.id+'_client', ack_callback);\n",
       "    return comm\n",
       "  }\n",
       "}\n",
       "\n",
       "HoloViewsWidget.prototype.process_msg = function(msg) {\n",
       "}\n",
       "\n",
       "function SelectionWidget(frames, id, slider_ids, keyMap, dim_vals, notFound, load_json, mode, cached, json_path, dynamic, plot_id){\n",
       "  this.frames = frames;\n",
       "  this.id = id;\n",
       "  this.plot_id = plot_id;\n",
       "  this.slider_ids = slider_ids;\n",
       "  this.keyMap = keyMap\n",
       "  this.current_frame = 0;\n",
       "  this.current_vals = dim_vals;\n",
       "  this.load_json = load_json;\n",
       "  this.mode = mode;\n",
       "  this.notFound = notFound;\n",
       "  this.cached = cached;\n",
       "  this.dynamic = dynamic;\n",
       "  this.cache = {};\n",
       "  this.json_path = json_path;\n",
       "  this.init_slider(this.current_vals[0]);\n",
       "  this.queue = [];\n",
       "  this.wait = false;\n",
       "  if (!this.cached || this.dynamic) {\n",
       "    this.comm = this.init_comms();\n",
       "  }\n",
       "}\n",
       "\n",
       "SelectionWidget.prototype = new HoloViewsWidget;\n",
       "\n",
       "\n",
       "SelectionWidget.prototype.get_key = function(current_vals) {\n",
       "  var key = \"(\";\n",
       "  for (var i=0; i<this.slider_ids.length; i++)\n",
       "  {\n",
       "    var val = this.current_vals[i];\n",
       "    if (!(typeof val === 'string')) {\n",
       "      if (val % 1 === 0) { val = val.toFixed(1); }\n",
       "      else { val = val.toFixed(10); val = val.slice(0, val.length-1);}\n",
       "    }\n",
       "    key += \"'\" + val + \"'\";\n",
       "    if(i != this.slider_ids.length-1) { key += ', ';}\n",
       "    else if(this.slider_ids.length == 1) { key += ',';}\n",
       "  }\n",
       "  key += \")\";\n",
       "  return this.keyMap[key];\n",
       "}\n",
       "\n",
       "SelectionWidget.prototype.set_frame = function(dim_val, dim_idx){\n",
       "  this.current_vals[dim_idx] = dim_val;\n",
       "  var key = this.current_vals;\n",
       "  if (!this.dynamic) {\n",
       "    key = this.get_key(key)\n",
       "  }\n",
       "  if (this.dynamic || !this.cached) {\n",
       "    if ((this.time !== undefined) && ((this.wait) && ((this.time + 10000) > Date.now()))) {\n",
       "      this.queue.push(key);\n",
       "      return\n",
       "    }\n",
       "    this.queue = [];\n",
       "    this.time = Date.now();\n",
       "    this.current_frame = key;\n",
       "    this.wait = true;\n",
       "    this.dynamic_update(key)\n",
       "  } else if (key !== undefined) {\n",
       "    this.update(key)\n",
       "  }\n",
       "}\n",
       "\n",
       "\n",
       "/* Define the ScrubberWidget class */\n",
       "function ScrubberWidget(frames, num_frames, id, interval, load_json, mode, cached, json_path, dynamic, plot_id){\n",
       "  this.slider_id = \"_anim_slider\" + id;\n",
       "  this.loop_select_id = \"_anim_loop_select\" + id;\n",
       "  this.id = id;\n",
       "  this.plot_id = plot_id;\n",
       "  this.interval = interval;\n",
       "  this.current_frame = 0;\n",
       "  this.direction = 0;\n",
       "  this.dynamic = dynamic;\n",
       "  this.timer = null;\n",
       "  this.load_json = load_json;\n",
       "  this.mode = mode;\n",
       "  this.cached = cached;\n",
       "  this.frames = frames;\n",
       "  this.cache = {};\n",
       "  this.length = num_frames;\n",
       "  this.json_path = json_path;\n",
       "  document.getElementById(this.slider_id).max = this.length - 1;\n",
       "  this.init_slider(0);\n",
       "  this.wait = false;\n",
       "  this.queue = [];\n",
       "  if (!this.cached || this.dynamic) {\n",
       "    this.comm = this.init_comms()\n",
       "  }\n",
       "}\n",
       "\n",
       "ScrubberWidget.prototype = new HoloViewsWidget;\n",
       "\n",
       "ScrubberWidget.prototype.set_frame = function(frame){\n",
       "  this.current_frame = frame;\n",
       "  var widget = document.getElementById(this.slider_id);\n",
       "  if (widget === null) {\n",
       "    this.pause_animation();\n",
       "    return\n",
       "  }\n",
       "  widget.value = this.current_frame;\n",
       "  if (this.dynamic || !this.cached) {\n",
       "    if ((this.time !== undefined) && ((this.wait) && ((this.time + 10000) > Date.now()))) {\n",
       "      this.queue.push(frame);\n",
       "      return\n",
       "    }\n",
       "    this.queue = [];\n",
       "    this.time = Date.now();\n",
       "    this.wait = true;\n",
       "    this.dynamic_update(frame)\n",
       "  } else {\n",
       "    this.update(frame)\n",
       "  }\n",
       "}\n",
       "\n",
       "ScrubberWidget.prototype.get_loop_state = function(){\n",
       "  var button_group = document[this.loop_select_id].state;\n",
       "  for (var i = 0; i < button_group.length; i++) {\n",
       "    var button = button_group[i];\n",
       "    if (button.checked) {\n",
       "      return button.value;\n",
       "    }\n",
       "  }\n",
       "  return undefined;\n",
       "}\n",
       "\n",
       "\n",
       "ScrubberWidget.prototype.next_frame = function() {\n",
       "  this.set_frame(Math.min(this.length - 1, this.current_frame + 1));\n",
       "}\n",
       "\n",
       "ScrubberWidget.prototype.previous_frame = function() {\n",
       "  this.set_frame(Math.max(0, this.current_frame - 1));\n",
       "}\n",
       "\n",
       "ScrubberWidget.prototype.first_frame = function() {\n",
       "  this.set_frame(0);\n",
       "}\n",
       "\n",
       "ScrubberWidget.prototype.last_frame = function() {\n",
       "  this.set_frame(this.length - 1);\n",
       "}\n",
       "\n",
       "ScrubberWidget.prototype.slower = function() {\n",
       "  this.interval /= 0.7;\n",
       "  if(this.direction > 0){this.play_animation();}\n",
       "  else if(this.direction < 0){this.reverse_animation();}\n",
       "}\n",
       "\n",
       "ScrubberWidget.prototype.faster = function() {\n",
       "  this.interval *= 0.7;\n",
       "  if(this.direction > 0){this.play_animation();}\n",
       "  else if(this.direction < 0){this.reverse_animation();}\n",
       "}\n",
       "\n",
       "ScrubberWidget.prototype.anim_step_forward = function() {\n",
       "  if(this.current_frame < this.length - 1){\n",
       "    this.next_frame();\n",
       "  }else{\n",
       "    var loop_state = this.get_loop_state();\n",
       "    if(loop_state == \"loop\"){\n",
       "      this.first_frame();\n",
       "    }else if(loop_state == \"reflect\"){\n",
       "      this.last_frame();\n",
       "      this.reverse_animation();\n",
       "    }else{\n",
       "      this.pause_animation();\n",
       "      this.last_frame();\n",
       "    }\n",
       "  }\n",
       "}\n",
       "\n",
       "ScrubberWidget.prototype.anim_step_reverse = function() {\n",
       "  if(this.current_frame > 0){\n",
       "    this.previous_frame();\n",
       "  } else {\n",
       "    var loop_state = this.get_loop_state();\n",
       "    if(loop_state == \"loop\"){\n",
       "      this.last_frame();\n",
       "    }else if(loop_state == \"reflect\"){\n",
       "      this.first_frame();\n",
       "      this.play_animation();\n",
       "    }else{\n",
       "      this.pause_animation();\n",
       "      this.first_frame();\n",
       "    }\n",
       "  }\n",
       "}\n",
       "\n",
       "ScrubberWidget.prototype.pause_animation = function() {\n",
       "  this.direction = 0;\n",
       "  if (this.timer){\n",
       "    clearInterval(this.timer);\n",
       "    this.timer = null;\n",
       "  }\n",
       "}\n",
       "\n",
       "ScrubberWidget.prototype.play_animation = function() {\n",
       "  this.pause_animation();\n",
       "  this.direction = 1;\n",
       "  var t = this;\n",
       "  if (!this.timer) this.timer = setInterval(function(){t.anim_step_forward();}, this.interval);\n",
       "}\n",
       "\n",
       "ScrubberWidget.prototype.reverse_animation = function() {\n",
       "  this.pause_animation();\n",
       "  this.direction = -1;\n",
       "  var t = this;\n",
       "  if (!this.timer) this.timer = setInterval(function(){t.anim_step_reverse();}, this.interval);\n",
       "}\n",
       "\n",
       "function extend(destination, source) {\n",
       "  for (var k in source) {\n",
       "    if (source.hasOwnProperty(k)) {\n",
       "      destination[k] = source[k];\n",
       "    }\n",
       "  }\n",
       "  return destination;\n",
       "}\n",
       "\n",
       "function update_widget(widget, values) {\n",
       "  if (widget.hasClass(\"ui-slider\")) {\n",
       "    widget.slider('option', {\n",
       "      min: 0,\n",
       "      max: values.length-1,\n",
       "      dim_vals: values,\n",
       "      value: 0,\n",
       "      dim_labels: values\n",
       "    })\n",
       "    widget.slider('option', 'slide').call(widget, event, {value: 0})\n",
       "  } else {\n",
       "    widget.empty();\n",
       "    for (var i=0; i<values.length; i++){\n",
       "      widget.append($(\"<option>\", {\n",
       "        value: i,\n",
       "        text: values[i]\n",
       "      }))\n",
       "    };\n",
       "    widget.data('values', values);\n",
       "    widget.data('value', 0);\n",
       "    widget.trigger(\"change\");\n",
       "  };\n",
       "}\n",
       "\n",
       "function init_slider(id, plot_id, dim, values, next_vals, labels, dynamic, step, value, next_dim,\n",
       "                     dim_idx, delay, jQueryUI_CDN, UNDERSCORE_CDN) {\n",
       "  // Slider JS Block START\n",
       "  function loadcssfile(filename){\n",
       "    var fileref=document.createElement(\"link\")\n",
       "    fileref.setAttribute(\"rel\", \"stylesheet\")\n",
       "    fileref.setAttribute(\"type\", \"text/css\")\n",
       "    fileref.setAttribute(\"href\", filename)\n",
       "    document.getElementsByTagName(\"head\")[0].appendChild(fileref)\n",
       "  }\n",
       "  loadcssfile(\"https://code.jquery.com/ui/1.10.4/themes/smoothness/jquery-ui.css\");\n",
       "  /* Check if jQuery and jQueryUI have been loaded\n",
       "     otherwise load with require.js */\n",
       "  var jQuery = window.jQuery,\n",
       "    // check for old versions of jQuery\n",
       "    oldjQuery = jQuery && !!jQuery.fn.jquery.match(/^1\\.[0-4](\\.|$)/),\n",
       "    jquery_path = '',\n",
       "    paths = {},\n",
       "    noConflict;\n",
       "  var jQueryUI = jQuery.ui;\n",
       "  // check for jQuery\n",
       "  if (!jQuery || oldjQuery) {\n",
       "    // load if it's not available or doesn't meet min standards\n",
       "    paths.jQuery = jQuery;\n",
       "    noConflict = !!oldjQuery;\n",
       "  } else {\n",
       "    // register the current jQuery\n",
       "    define('jquery', [], function() { return jQuery; });\n",
       "  }\n",
       "  if (!jQueryUI) {\n",
       "    paths.jQueryUI = jQueryUI_CDN.slice(null, -3);\n",
       "  } else {\n",
       "    define('jQueryUI', [], function() { return jQuery.ui; });\n",
       "  }\n",
       "  paths.underscore = UNDERSCORE_CDN.slice(null, -3);\n",
       "  var jquery_require = {\n",
       "    paths: paths,\n",
       "    shim: {\n",
       "      \"jQueryUI\": {\n",
       "        exports:\"$\",\n",
       "        deps: ['jquery']\n",
       "      },\n",
       "      \"underscore\": {\n",
       "        exports: '_'\n",
       "      }\n",
       "    }\n",
       "  }\n",
       "  require.config(jquery_require);\n",
       "  require([\"jQueryUI\", \"underscore\"], function(jUI, _){\n",
       "    if (noConflict) $.noConflict(true);\n",
       "    var vals = values;\n",
       "    if (dynamic && vals.constructor === Array) {\n",
       "      var default_value = parseFloat(value);\n",
       "      var min = parseFloat(vals[0]);\n",
       "      var max = parseFloat(vals[vals.length-1]);\n",
       "      var wstep = step;\n",
       "      var wlabels = [default_value];\n",
       "      var init_label = default_value;\n",
       "    } else {\n",
       "      var min = 0;\n",
       "      if (dynamic) {\n",
       "        var max = Object.keys(vals).length - 1;\n",
       "        var init_label = labels[value];\n",
       "        var default_value = values[value];\n",
       "      } else {\n",
       "        var max = vals.length - 1;\n",
       "        var init_label = labels[value];\n",
       "        var default_value = value;\n",
       "      }\n",
       "      var wstep = 1;\n",
       "      var wlabels = labels;\n",
       "    }\n",
       "    function adjustFontSize(text) {\n",
       "      var width_ratio = (text.parent().width()/8)/text.val().length;\n",
       "      var size = Math.min(0.9, Math.max(0.6, width_ratio))+'em';\n",
       "      text.css('font-size', size);\n",
       "    }\n",
       "    var slider = $('#_anim_widget'+id+'_'+dim);\n",
       "    slider.slider({\n",
       "      animate: \"fast\",\n",
       "      min: min,\n",
       "      max: max,\n",
       "      step: wstep,\n",
       "      value: default_value,\n",
       "      dim_vals: vals,\n",
       "      dim_labels: wlabels,\n",
       "      next_vals: next_vals,\n",
       "      slide: function(event, ui) {\n",
       "        var vals = slider.slider(\"option\", \"dim_vals\");\n",
       "        var next_vals = slider.slider(\"option\", \"next_vals\");\n",
       "        var dlabels = slider.slider(\"option\", \"dim_labels\");\n",
       "        if (dynamic) {\n",
       "          var dim_val = ui.value;\n",
       "          if (vals.constructor === Array) {\n",
       "            var label = ui.value;\n",
       "          } else {\n",
       "            var label = dlabels[ui.value];\n",
       "          }\n",
       "        } else {\n",
       "          var dim_val = vals[ui.value];\n",
       "          var label = dlabels[ui.value];\n",
       "        }\n",
       "        var text = $('#textInput'+id+'_'+dim);\n",
       "        text.val(label);\n",
       "        adjustFontSize(text);\n",
       "        HoloViews.index[plot_id].set_frame(dim_val, dim_idx);\n",
       "        if (Object.keys(next_vals).length > 0) {\n",
       "          var new_vals = next_vals[dim_val];\n",
       "          var next_widget = $('#_anim_widget'+id+'_'+next_dim);\n",
       "          update_widget(next_widget, new_vals);\n",
       "        }\n",
       "      }\n",
       "    });\n",
       "    slider.keypress(function(event) {\n",
       "      if (event.which == 80 || event.which == 112) {\n",
       "        var start = slider.slider(\"option\", \"value\");\n",
       "        var stop =  slider.slider(\"option\", \"max\");\n",
       "        for (var i=start; i<=stop; i++) {\n",
       "          var delay = i*delay;\n",
       "          $.proxy(function doSetTimeout(i) { setTimeout($.proxy(function() {\n",
       "            var val = {value:i};\n",
       "            slider.slider('value',i);\n",
       "            slider.slider(\"option\", \"slide\")(null, val);\n",
       "          }, slider), delay);}, slider)(i);\n",
       "        }\n",
       "      }\n",
       "      if (event.which == 82 || event.which == 114) {\n",
       "        var start = slider.slider(\"option\", \"value\");\n",
       "        var stop =  slider.slider(\"option\", \"min\");\n",
       "        var count = 0;\n",
       "        for (var i=start; i>=stop; i--) {\n",
       "          var delay = count*delay;\n",
       "          count = count + 1;\n",
       "          $.proxy(function doSetTimeout(i) { setTimeout($.proxy(function() {\n",
       "            var val = {value:i};\n",
       "            slider.slider('value',i);\n",
       "            slider.slider(\"option\", \"slide\")(null, val);\n",
       "          }, slider), delay);}, slider)(i);\n",
       "        }\n",
       "      }\n",
       "    });\n",
       "    var textInput = $('#textInput'+id+'_'+dim)\n",
       "    textInput.val(init_label);\n",
       "    adjustFontSize(textInput);\n",
       "  });\n",
       "}\n",
       "\n",
       "function init_dropdown(id, plot_id, dim, vals, value, next_vals, labels, next_dim, dim_idx, dynamic) {\n",
       "  var widget = $(\"#_anim_widget\"+id+'_'+dim);\n",
       "  widget.data('values', vals)\n",
       "  for (var i=0; i<vals.length; i++){\n",
       "    if (dynamic) {\n",
       "      var val = vals[i];\n",
       "    } else {\n",
       "      var val = i;\n",
       "    }\n",
       "    widget.append($(\"<option>\", {\n",
       "      value: val,\n",
       "      text: labels[i]\n",
       "    }));\n",
       "  };\n",
       "  widget.data(\"next_vals\", next_vals);\n",
       "  widget.val(value);\n",
       "  widget.on('change', function(event, ui) {\n",
       "    if (dynamic) {\n",
       "      var dim_val = parseInt(this.value);\n",
       "    } else {\n",
       "      var dim_val = $.data(this, 'values')[this.value];\n",
       "    }\n",
       "    var next_vals = $.data(this, \"next_vals\");\n",
       "    if (Object.keys(next_vals).length > 0) {\n",
       "      var new_vals = next_vals[dim_val];\n",
       "      var next_widget = $('#_anim_widget'+id+'_'+next_dim);\n",
       "      update_widget(next_widget, new_vals);\n",
       "    }\n",
       "    var widgets = HoloViews.index[plot_id]\n",
       "    if (widgets) {\n",
       "      widgets.set_frame(dim_val, dim_idx);\n",
       "    }\n",
       "  });\n",
       "}\n",
       "\n",
       "\n",
       "if (window.HoloViews === undefined) {\n",
       "  window.HoloViews = {}\n",
       "  window.PyViz = window.HoloViews\n",
       "} else if (window.PyViz === undefined) {\n",
       "  window.PyViz = window.HoloViews\n",
       "}\n",
       "\n",
       "\n",
       "var _namespace = {\n",
       "  init_slider: init_slider,\n",
       "  init_dropdown: init_dropdown,\n",
       "  comms: {},\n",
       "  comm_status: {},\n",
       "  index: {},\n",
       "  plot_index: {},\n",
       "  kernels: {},\n",
       "  receivers: {}\n",
       "}\n",
       "\n",
       "for (var k in _namespace) {\n",
       "  if (!(k in window.HoloViews)) {\n",
       "    window.HoloViews[k] = _namespace[k];\n",
       "  }\n",
       "}\n",
       "\n",
       "var JS_MIME_TYPE = 'application/javascript';\n",
       "var HTML_MIME_TYPE = 'text/html';\n",
       "var EXEC_MIME_TYPE = 'application/vnd.holoviews_exec.v0+json';\n",
       "var CLASS_NAME = 'output';\n",
       "\n",
       "/**\n",
       " * Render data to the DOM node\n",
       " */\n",
       "function render(props, node) {\n",
       "  var div = document.createElement(\"div\");\n",
       "  var script = document.createElement(\"script\");\n",
       "  node.appendChild(div);\n",
       "  node.appendChild(script);\n",
       "}\n",
       "\n",
       "/**\n",
       " * Handle when a new output is added\n",
       " */\n",
       "function handle_add_output(event, handle) {\n",
       "  var output_area = handle.output_area;\n",
       "  var output = handle.output;\n",
       "  if (!output.data.hasOwnProperty(EXEC_MIME_TYPE)) {\n",
       "    return\n",
       "  }\n",
       "  var id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n",
       "  var toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n",
       "  if (id !== undefined) {\n",
       "    var nchildren = toinsert.length;\n",
       "    toinsert[nchildren-1].children[0].innerHTML = output.data[HTML_MIME_TYPE];\n",
       "    toinsert[nchildren-1].children[1].textContent = output.data[JS_MIME_TYPE];\n",
       "    output_area._hv_plot_id = id;\n",
       "    if ((window.Bokeh !== undefined) && (id in Bokeh.index)) {\n",
       "      HoloViews.plot_index[id] = Bokeh.index[id];\n",
       "    } else {\n",
       "      HoloViews.plot_index[id] = null;\n",
       "    }\n",
       "  }\n",
       "}\n",
       "\n",
       "/**\n",
       " * Handle when an output is cleared or removed\n",
       " */\n",
       "function handle_clear_output(event, handle) {\n",
       "  var id = handle.cell.output_area._hv_plot_id;\n",
       "  if ((id === undefined) || !(id in HoloViews.plot_index)) { return; }\n",
       "  var comm = window.HoloViews.comm_manager.get_client_comm(\"hv-extension-comm\", \"hv-extension-comm\", function () {});\n",
       "  if (comm !== null) {\n",
       "    comm.send({event_type: 'delete', 'id': id});\n",
       "  }\n",
       "  delete HoloViews.plot_index[id];\n",
       "  if ((window.Bokeh !== undefined) & (id in window.Bokeh.index)) {\n",
       "    window.Bokeh.index[id].model.document.clear();\n",
       "    delete Bokeh.index[id];\n",
       "  }\n",
       "}\n",
       "\n",
       "/**\n",
       " * Handle kernel restart event\n",
       " */\n",
       "function handle_kernel_cleanup(event, handle) {\n",
       "  delete HoloViews.comms[\"hv-extension-comm\"];\n",
       "  window.HoloViews.plot_index = {}\n",
       "}\n",
       "\n",
       "/**\n",
       " * Handle update_display_data messages\n",
       " */\n",
       "function handle_update_output(event, handle) {\n",
       "  handle_clear_output(event, {cell: {output_area: handle.output_area}})\n",
       "  handle_add_output(event, handle)\n",
       "}\n",
       "\n",
       "function register_renderer(events, OutputArea) {\n",
       "  function append_mime(data, metadata, element) {\n",
       "    // create a DOM node to render to\n",
       "    var toinsert = this.create_output_subarea(\n",
       "    metadata,\n",
       "    CLASS_NAME,\n",
       "    EXEC_MIME_TYPE\n",
       "    );\n",
       "    this.keyboard_manager.register_events(toinsert);\n",
       "    // Render to node\n",
       "    var props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n",
       "    render(props, toinsert[0]);\n",
       "    element.append(toinsert);\n",
       "    return toinsert\n",
       "  }\n",
       "\n",
       "  events.on('output_added.OutputArea', handle_add_output);\t\n",
       "  events.on('output_updated.OutputArea', handle_update_output);\n",
       "  events.on('clear_output.CodeCell', handle_clear_output);\n",
       "  events.on('delete.Cell', handle_clear_output);\n",
       "  events.on('kernel_ready.Kernel', handle_kernel_cleanup);\n",
       "\n",
       "  OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n",
       "    safe: true,\n",
       "    index: 0\n",
       "  });\n",
       "}\n",
       "\n",
       "if (window.Jupyter !== undefined) {\n",
       "  try {\n",
       "    var events = require('base/js/events');\n",
       "    var OutputArea = require('notebook/js/outputarea').OutputArea;\n",
       "    if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n",
       "      register_renderer(events, OutputArea);\n",
       "    }\n",
       "  } catch(err) {\n",
       "  }\n",
       "}\n",
       "\n",
       "// Define MPL specific subclasses\n",
       "function MPLSelectionWidget() {\n",
       "  SelectionWidget.apply(this, arguments);\n",
       "}\n",
       "\n",
       "function MPLScrubberWidget() {\n",
       "  ScrubberWidget.apply(this, arguments);\n",
       "}\n",
       "\n",
       "// Let them inherit from the baseclasses\n",
       "MPLSelectionWidget.prototype = Object.create(SelectionWidget.prototype);\n",
       "MPLScrubberWidget.prototype = Object.create(ScrubberWidget.prototype);\n",
       "\n",
       "// Define methods to override on widgets\n",
       "var MPLMethods = {\n",
       "  init_slider : function(init_val){\n",
       "    if(this.load_json) {\n",
       "      this.from_json()\n",
       "    } else {\n",
       "      this.update_cache();\n",
       "    }\n",
       "    if (this.dynamic | !this.cached | (this.current_vals === undefined)) {\n",
       "      this.update(0)\n",
       "    } else {\n",
       "      this.set_frame(this.current_vals[0], 0)\n",
       "    }\n",
       "  },\n",
       "  process_msg : function(msg) {\n",
       "    var data = msg.content.data;\n",
       "    this.frames[this.current] = data;\n",
       "    this.update_cache(true);\n",
       "    this.update(this.current);\n",
       "  }\n",
       "}\n",
       "// Extend MPL widgets with backend specific methods\n",
       "extend(MPLSelectionWidget.prototype, MPLMethods);\n",
       "extend(MPLScrubberWidget.prototype, MPLMethods);\n",
       "\n",
       "window.HoloViews.MPLSelectionWidget = MPLSelectionWidget\n",
       "window.HoloViews.MPLScrubberWidget = MPLScrubberWidget\n",
       "\n",
       "// Define Bokeh specific subclasses\n",
       "function BokehSelectionWidget() {\n",
       "  SelectionWidget.apply(this, arguments);\n",
       "}\n",
       "\n",
       "function BokehScrubberWidget() {\n",
       "  ScrubberWidget.apply(this, arguments);\n",
       "}\n",
       "\n",
       "// Let them inherit from the baseclasses\n",
       "BokehSelectionWidget.prototype = Object.create(SelectionWidget.prototype);\n",
       "BokehScrubberWidget.prototype = Object.create(ScrubberWidget.prototype);\n",
       "\n",
       "// Define methods to override on widgets\n",
       "var BokehMethods = {\n",
       "  update_cache : function(){\n",
       "    for (var index in this.frames) {\n",
       "      this.frames[index] = JSON.parse(this.frames[index]);\n",
       "    }\n",
       "  },\n",
       "  update : function(current){\n",
       "    if (current === undefined) {\n",
       "      return;\n",
       "    }\n",
       "    var data = this.frames[current];\n",
       "    if (data !== undefined) {\n",
       "      if (data.root in HoloViews.plot_index) {\n",
       "        var doc = HoloViews.plot_index[data.root].model.document;\n",
       "      } else {\n",
       "        var doc = Bokeh.index[data.root].model.document;\n",
       "      }\n",
       "      doc.apply_json_patch(data.content);\n",
       "    }\n",
       "  },\n",
       "  init_comms: function() {\n",
       "    if (Bokeh.protocol !== undefined) {\n",
       "      this.receiver = new Bokeh.protocol.Receiver()\n",
       "    } else {\n",
       "      this.receiver = null;\n",
       "    }\n",
       "    return HoloViewsWidget.prototype.init_comms.call(this);\n",
       "  },\n",
       "  process_msg : function(msg) {\n",
       "    if (this.plot_id in HoloViews.plot_index) {\n",
       "      var doc = HoloViews.plot_index[this.plot_id].model.document;\n",
       "    } else {\n",
       "      var doc = Bokeh.index[this.plot_id].model.document;\n",
       "    }\n",
       "    if (this.receiver === null) { return }\n",
       "    var receiver = this.receiver;\n",
       "    if (msg.buffers.length > 0) {\n",
       "      receiver.consume(msg.buffers[0].buffer)\n",
       "    } else {\n",
       "      receiver.consume(msg.content.data)\n",
       "    }\n",
       "    const comm_msg = receiver.message;\n",
       "    if ((comm_msg != null) && (doc != null)) {\n",
       "      doc.apply_json_patch(comm_msg.content, comm_msg.buffers)\n",
       "    }\n",
       "  }\n",
       "}\n",
       "\n",
       "// Extend Bokeh widgets with backend specific methods\n",
       "extend(BokehSelectionWidget.prototype, BokehMethods);\n",
       "extend(BokehScrubberWidget.prototype, BokehMethods);\n",
       "\n",
       "window.HoloViews.BokehSelectionWidget = BokehSelectionWidget\n",
       "window.HoloViews.BokehScrubberWidget = BokehScrubberWidget\n",
       "\n",
       "    function JupyterCommManager() {\n",
       "    }\n",
       "\n",
       "    JupyterCommManager.prototype.register_target = function(plot_id, comm_id, msg_handler) {\n",
       "      if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n",
       "        var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n",
       "        comm_manager.register_target(comm_id, function(comm) {\n",
       "          comm.on_msg(msg_handler);\n",
       "        });\n",
       "      } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n",
       "        window.PyViz.kernels[plot_id].registerCommTarget(comm_id, function(comm) {\n",
       "          comm.onMsg = msg_handler;\n",
       "        });\n",
       "      }\n",
       "    }\n",
       "\n",
       "    JupyterCommManager.prototype.get_client_comm = function(plot_id, comm_id, msg_handler) {\n",
       "      if (comm_id in window.PyViz.comms) {\n",
       "        return window.PyViz.comms[comm_id];\n",
       "      } else if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n",
       "        var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n",
       "        var comm = comm_manager.new_comm(comm_id, {}, {}, {}, comm_id);\n",
       "        if (msg_handler) {\n",
       "          comm.on_msg(msg_handler);\n",
       "        }\n",
       "      } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n",
       "        var comm = window.PyViz.kernels[plot_id].connectToComm(comm_id);\n",
       "        comm.open();\n",
       "        if (msg_handler) {\n",
       "          comm.onMsg = msg_handler;\n",
       "        }\n",
       "      }\n",
       "\n",
       "      window.PyViz.comms[comm_id] = comm;\n",
       "      return comm;\n",
       "    }\n",
       "\n",
       "    window.PyViz.comm_manager = new JupyterCommManager();\n",
       "    "
      ],
      "application/vnd.holoviews_load.v0+json": "function HoloViewsWidget() {\n}\n\nHoloViewsWidget.prototype.init_slider = function(init_val){\n  if(this.load_json) {\n    this.from_json()\n  } else {\n    this.update_cache();\n  }\n}\n\nHoloViewsWidget.prototype.populate_cache = function(idx){\n  this.cache[idx].innerHTML = this.frames[idx];\n  if (this.embed) {\n    delete this.frames[idx];\n  }\n}\n\nHoloViewsWidget.prototype.process_error = function(msg){\n}\n\nHoloViewsWidget.prototype.from_json = function() {\n  var data_url = this.json_path + this.id + '.json';\n  $.getJSON(data_url, $.proxy(function(json_data) {\n    this.frames = json_data;\n    this.update_cache();\n    this.update(0);\n  }, this));\n}\n\nHoloViewsWidget.prototype.dynamic_update = function(current){\n  if (current === undefined) {\n    return\n  }\n  this.current = current;\n  if (this.comm) {\n    var msg = {comm_id: this.id+'_client', content: current}\n    this.comm.send(msg);\n  }\n}\n\nHoloViewsWidget.prototype.update_cache = function(force){\n  var frame_len = Object.keys(this.frames).length;\n  for (var i=0; i<frame_len; i++) {\n    if(!this.load_json || this.dynamic)  {\n      var frame = Object.keys(this.frames)[i];\n    } else {\n      var frame = i;\n    }\n    if(!(frame in this.cache) || force) {\n      if ((frame in this.cache) && force) { this.cache[frame].remove() }\n      var div = document.createElement(\"div\");\n      var parent = document.getElementById(\"_anim_img\"+this.id);\n      div.style.display = \"none\";\n      parent.appendChild(div)\n      this.cache[frame] = div;\n      var cache_id = \"_anim_img\"+this.id+\"_\"+frame;\n      this.populate_cache(frame);\n    }\n  }\n}\n\nHoloViewsWidget.prototype.update = function(current){\n  if(current in this.cache) {\n    for (var index in this.cache) {\n      this.cache[index].style.display = \"none\";\n    }\n    this.cache[current].style.display = \"\";\n    this.wait = false;\n  }\n}\n\nHoloViewsWidget.prototype.init_comms = function() {\n  var that = this\n  HoloViews.comm_manager.register_target(this.plot_id, this.id, function (msg) { that.process_msg(msg) })\n  if (!this.cached || this.dynamic) {\n    function ack_callback(msg) {\n      msg = JSON.parse(msg.content.data);\n      var comm_id = msg[\"comm_id\"]\n      var comm_status = HoloViews.comm_status[comm_id];\n      if (that.queue.length > 0) {\n        that.time = Date.now();\n        that.dynamic_update(that.queue[that.queue.length-1]);\n        that.queue = [];\n      } else {\n        that.wait = false;\n      }\n      if ((msg.msg_type == \"Ready\") && msg.content) {\n        console.log(\"Python callback returned following output:\", msg.content);\n      } else if (msg.msg_type == \"Error\") {\n        console.log(\"Python failed with the following traceback:\", msg['traceback'])\n      }\n    }\n    var comm = HoloViews.comm_manager.get_client_comm(this.plot_id, this.id+'_client', ack_callback);\n    return comm\n  }\n}\n\nHoloViewsWidget.prototype.process_msg = function(msg) {\n}\n\nfunction SelectionWidget(frames, id, slider_ids, keyMap, dim_vals, notFound, load_json, mode, cached, json_path, dynamic, plot_id){\n  this.frames = frames;\n  this.id = id;\n  this.plot_id = plot_id;\n  this.slider_ids = slider_ids;\n  this.keyMap = keyMap\n  this.current_frame = 0;\n  this.current_vals = dim_vals;\n  this.load_json = load_json;\n  this.mode = mode;\n  this.notFound = notFound;\n  this.cached = cached;\n  this.dynamic = dynamic;\n  this.cache = {};\n  this.json_path = json_path;\n  this.init_slider(this.current_vals[0]);\n  this.queue = [];\n  this.wait = false;\n  if (!this.cached || this.dynamic) {\n    this.comm = this.init_comms();\n  }\n}\n\nSelectionWidget.prototype = new HoloViewsWidget;\n\n\nSelectionWidget.prototype.get_key = function(current_vals) {\n  var key = \"(\";\n  for (var i=0; i<this.slider_ids.length; i++)\n  {\n    var val = this.current_vals[i];\n    if (!(typeof val === 'string')) {\n      if (val % 1 === 0) { val = val.toFixed(1); }\n      else { val = val.toFixed(10); val = val.slice(0, val.length-1);}\n    }\n    key += \"'\" + val + \"'\";\n    if(i != this.slider_ids.length-1) { key += ', ';}\n    else if(this.slider_ids.length == 1) { key += ',';}\n  }\n  key += \")\";\n  return this.keyMap[key];\n}\n\nSelectionWidget.prototype.set_frame = function(dim_val, dim_idx){\n  this.current_vals[dim_idx] = dim_val;\n  var key = this.current_vals;\n  if (!this.dynamic) {\n    key = this.get_key(key)\n  }\n  if (this.dynamic || !this.cached) {\n    if ((this.time !== undefined) && ((this.wait) && ((this.time + 10000) > Date.now()))) {\n      this.queue.push(key);\n      return\n    }\n    this.queue = [];\n    this.time = Date.now();\n    this.current_frame = key;\n    this.wait = true;\n    this.dynamic_update(key)\n  } else if (key !== undefined) {\n    this.update(key)\n  }\n}\n\n\n/* Define the ScrubberWidget class */\nfunction ScrubberWidget(frames, num_frames, id, interval, load_json, mode, cached, json_path, dynamic, plot_id){\n  this.slider_id = \"_anim_slider\" + id;\n  this.loop_select_id = \"_anim_loop_select\" + id;\n  this.id = id;\n  this.plot_id = plot_id;\n  this.interval = interval;\n  this.current_frame = 0;\n  this.direction = 0;\n  this.dynamic = dynamic;\n  this.timer = null;\n  this.load_json = load_json;\n  this.mode = mode;\n  this.cached = cached;\n  this.frames = frames;\n  this.cache = {};\n  this.length = num_frames;\n  this.json_path = json_path;\n  document.getElementById(this.slider_id).max = this.length - 1;\n  this.init_slider(0);\n  this.wait = false;\n  this.queue = [];\n  if (!this.cached || this.dynamic) {\n    this.comm = this.init_comms()\n  }\n}\n\nScrubberWidget.prototype = new HoloViewsWidget;\n\nScrubberWidget.prototype.set_frame = function(frame){\n  this.current_frame = frame;\n  var widget = document.getElementById(this.slider_id);\n  if (widget === null) {\n    this.pause_animation();\n    return\n  }\n  widget.value = this.current_frame;\n  if (this.dynamic || !this.cached) {\n    if ((this.time !== undefined) && ((this.wait) && ((this.time + 10000) > Date.now()))) {\n      this.queue.push(frame);\n      return\n    }\n    this.queue = [];\n    this.time = Date.now();\n    this.wait = true;\n    this.dynamic_update(frame)\n  } else {\n    this.update(frame)\n  }\n}\n\nScrubberWidget.prototype.get_loop_state = function(){\n  var button_group = document[this.loop_select_id].state;\n  for (var i = 0; i < button_group.length; i++) {\n    var button = button_group[i];\n    if (button.checked) {\n      return button.value;\n    }\n  }\n  return undefined;\n}\n\n\nScrubberWidget.prototype.next_frame = function() {\n  this.set_frame(Math.min(this.length - 1, this.current_frame + 1));\n}\n\nScrubberWidget.prototype.previous_frame = function() {\n  this.set_frame(Math.max(0, this.current_frame - 1));\n}\n\nScrubberWidget.prototype.first_frame = function() {\n  this.set_frame(0);\n}\n\nScrubberWidget.prototype.last_frame = function() {\n  this.set_frame(this.length - 1);\n}\n\nScrubberWidget.prototype.slower = function() {\n  this.interval /= 0.7;\n  if(this.direction > 0){this.play_animation();}\n  else if(this.direction < 0){this.reverse_animation();}\n}\n\nScrubberWidget.prototype.faster = function() {\n  this.interval *= 0.7;\n  if(this.direction > 0){this.play_animation();}\n  else if(this.direction < 0){this.reverse_animation();}\n}\n\nScrubberWidget.prototype.anim_step_forward = function() {\n  if(this.current_frame < this.length - 1){\n    this.next_frame();\n  }else{\n    var loop_state = this.get_loop_state();\n    if(loop_state == \"loop\"){\n      this.first_frame();\n    }else if(loop_state == \"reflect\"){\n      this.last_frame();\n      this.reverse_animation();\n    }else{\n      this.pause_animation();\n      this.last_frame();\n    }\n  }\n}\n\nScrubberWidget.prototype.anim_step_reverse = function() {\n  if(this.current_frame > 0){\n    this.previous_frame();\n  } else {\n    var loop_state = this.get_loop_state();\n    if(loop_state == \"loop\"){\n      this.last_frame();\n    }else if(loop_state == \"reflect\"){\n      this.first_frame();\n      this.play_animation();\n    }else{\n      this.pause_animation();\n      this.first_frame();\n    }\n  }\n}\n\nScrubberWidget.prototype.pause_animation = function() {\n  this.direction = 0;\n  if (this.timer){\n    clearInterval(this.timer);\n    this.timer = null;\n  }\n}\n\nScrubberWidget.prototype.play_animation = function() {\n  this.pause_animation();\n  this.direction = 1;\n  var t = this;\n  if (!this.timer) this.timer = setInterval(function(){t.anim_step_forward();}, this.interval);\n}\n\nScrubberWidget.prototype.reverse_animation = function() {\n  this.pause_animation();\n  this.direction = -1;\n  var t = this;\n  if (!this.timer) this.timer = setInterval(function(){t.anim_step_reverse();}, this.interval);\n}\n\nfunction extend(destination, source) {\n  for (var k in source) {\n    if (source.hasOwnProperty(k)) {\n      destination[k] = source[k];\n    }\n  }\n  return destination;\n}\n\nfunction update_widget(widget, values) {\n  if (widget.hasClass(\"ui-slider\")) {\n    widget.slider('option', {\n      min: 0,\n      max: values.length-1,\n      dim_vals: values,\n      value: 0,\n      dim_labels: values\n    })\n    widget.slider('option', 'slide').call(widget, event, {value: 0})\n  } else {\n    widget.empty();\n    for (var i=0; i<values.length; i++){\n      widget.append($(\"<option>\", {\n        value: i,\n        text: values[i]\n      }))\n    };\n    widget.data('values', values);\n    widget.data('value', 0);\n    widget.trigger(\"change\");\n  };\n}\n\nfunction init_slider(id, plot_id, dim, values, next_vals, labels, dynamic, step, value, next_dim,\n                     dim_idx, delay, jQueryUI_CDN, UNDERSCORE_CDN) {\n  // Slider JS Block START\n  function loadcssfile(filename){\n    var fileref=document.createElement(\"link\")\n    fileref.setAttribute(\"rel\", \"stylesheet\")\n    fileref.setAttribute(\"type\", \"text/css\")\n    fileref.setAttribute(\"href\", filename)\n    document.getElementsByTagName(\"head\")[0].appendChild(fileref)\n  }\n  loadcssfile(\"https://code.jquery.com/ui/1.10.4/themes/smoothness/jquery-ui.css\");\n  /* Check if jQuery and jQueryUI have been loaded\n     otherwise load with require.js */\n  var jQuery = window.jQuery,\n    // check for old versions of jQuery\n    oldjQuery = jQuery && !!jQuery.fn.jquery.match(/^1\\.[0-4](\\.|$)/),\n    jquery_path = '',\n    paths = {},\n    noConflict;\n  var jQueryUI = jQuery.ui;\n  // check for jQuery\n  if (!jQuery || oldjQuery) {\n    // load if it's not available or doesn't meet min standards\n    paths.jQuery = jQuery;\n    noConflict = !!oldjQuery;\n  } else {\n    // register the current jQuery\n    define('jquery', [], function() { return jQuery; });\n  }\n  if (!jQueryUI) {\n    paths.jQueryUI = jQueryUI_CDN.slice(null, -3);\n  } else {\n    define('jQueryUI', [], function() { return jQuery.ui; });\n  }\n  paths.underscore = UNDERSCORE_CDN.slice(null, -3);\n  var jquery_require = {\n    paths: paths,\n    shim: {\n      \"jQueryUI\": {\n        exports:\"$\",\n        deps: ['jquery']\n      },\n      \"underscore\": {\n        exports: '_'\n      }\n    }\n  }\n  require.config(jquery_require);\n  require([\"jQueryUI\", \"underscore\"], function(jUI, _){\n    if (noConflict) $.noConflict(true);\n    var vals = values;\n    if (dynamic && vals.constructor === Array) {\n      var default_value = parseFloat(value);\n      var min = parseFloat(vals[0]);\n      var max = parseFloat(vals[vals.length-1]);\n      var wstep = step;\n      var wlabels = [default_value];\n      var init_label = default_value;\n    } else {\n      var min = 0;\n      if (dynamic) {\n        var max = Object.keys(vals).length - 1;\n        var init_label = labels[value];\n        var default_value = values[value];\n      } else {\n        var max = vals.length - 1;\n        var init_label = labels[value];\n        var default_value = value;\n      }\n      var wstep = 1;\n      var wlabels = labels;\n    }\n    function adjustFontSize(text) {\n      var width_ratio = (text.parent().width()/8)/text.val().length;\n      var size = Math.min(0.9, Math.max(0.6, width_ratio))+'em';\n      text.css('font-size', size);\n    }\n    var slider = $('#_anim_widget'+id+'_'+dim);\n    slider.slider({\n      animate: \"fast\",\n      min: min,\n      max: max,\n      step: wstep,\n      value: default_value,\n      dim_vals: vals,\n      dim_labels: wlabels,\n      next_vals: next_vals,\n      slide: function(event, ui) {\n        var vals = slider.slider(\"option\", \"dim_vals\");\n        var next_vals = slider.slider(\"option\", \"next_vals\");\n        var dlabels = slider.slider(\"option\", \"dim_labels\");\n        if (dynamic) {\n          var dim_val = ui.value;\n          if (vals.constructor === Array) {\n            var label = ui.value;\n          } else {\n            var label = dlabels[ui.value];\n          }\n        } else {\n          var dim_val = vals[ui.value];\n          var label = dlabels[ui.value];\n        }\n        var text = $('#textInput'+id+'_'+dim);\n        text.val(label);\n        adjustFontSize(text);\n        HoloViews.index[plot_id].set_frame(dim_val, dim_idx);\n        if (Object.keys(next_vals).length > 0) {\n          var new_vals = next_vals[dim_val];\n          var next_widget = $('#_anim_widget'+id+'_'+next_dim);\n          update_widget(next_widget, new_vals);\n        }\n      }\n    });\n    slider.keypress(function(event) {\n      if (event.which == 80 || event.which == 112) {\n        var start = slider.slider(\"option\", \"value\");\n        var stop =  slider.slider(\"option\", \"max\");\n        for (var i=start; i<=stop; i++) {\n          var delay = i*delay;\n          $.proxy(function doSetTimeout(i) { setTimeout($.proxy(function() {\n            var val = {value:i};\n            slider.slider('value',i);\n            slider.slider(\"option\", \"slide\")(null, val);\n          }, slider), delay);}, slider)(i);\n        }\n      }\n      if (event.which == 82 || event.which == 114) {\n        var start = slider.slider(\"option\", \"value\");\n        var stop =  slider.slider(\"option\", \"min\");\n        var count = 0;\n        for (var i=start; i>=stop; i--) {\n          var delay = count*delay;\n          count = count + 1;\n          $.proxy(function doSetTimeout(i) { setTimeout($.proxy(function() {\n            var val = {value:i};\n            slider.slider('value',i);\n            slider.slider(\"option\", \"slide\")(null, val);\n          }, slider), delay);}, slider)(i);\n        }\n      }\n    });\n    var textInput = $('#textInput'+id+'_'+dim)\n    textInput.val(init_label);\n    adjustFontSize(textInput);\n  });\n}\n\nfunction init_dropdown(id, plot_id, dim, vals, value, next_vals, labels, next_dim, dim_idx, dynamic) {\n  var widget = $(\"#_anim_widget\"+id+'_'+dim);\n  widget.data('values', vals)\n  for (var i=0; i<vals.length; i++){\n    if (dynamic) {\n      var val = vals[i];\n    } else {\n      var val = i;\n    }\n    widget.append($(\"<option>\", {\n      value: val,\n      text: labels[i]\n    }));\n  };\n  widget.data(\"next_vals\", next_vals);\n  widget.val(value);\n  widget.on('change', function(event, ui) {\n    if (dynamic) {\n      var dim_val = parseInt(this.value);\n    } else {\n      var dim_val = $.data(this, 'values')[this.value];\n    }\n    var next_vals = $.data(this, \"next_vals\");\n    if (Object.keys(next_vals).length > 0) {\n      var new_vals = next_vals[dim_val];\n      var next_widget = $('#_anim_widget'+id+'_'+next_dim);\n      update_widget(next_widget, new_vals);\n    }\n    var widgets = HoloViews.index[plot_id]\n    if (widgets) {\n      widgets.set_frame(dim_val, dim_idx);\n    }\n  });\n}\n\n\nif (window.HoloViews === undefined) {\n  window.HoloViews = {}\n  window.PyViz = window.HoloViews\n} else if (window.PyViz === undefined) {\n  window.PyViz = window.HoloViews\n}\n\n\nvar _namespace = {\n  init_slider: init_slider,\n  init_dropdown: init_dropdown,\n  comms: {},\n  comm_status: {},\n  index: {},\n  plot_index: {},\n  kernels: {},\n  receivers: {}\n}\n\nfor (var k in _namespace) {\n  if (!(k in window.HoloViews)) {\n    window.HoloViews[k] = _namespace[k];\n  }\n}\n\nvar JS_MIME_TYPE = 'application/javascript';\nvar HTML_MIME_TYPE = 'text/html';\nvar EXEC_MIME_TYPE = 'application/vnd.holoviews_exec.v0+json';\nvar CLASS_NAME = 'output';\n\n/**\n * Render data to the DOM node\n */\nfunction render(props, node) {\n  var div = document.createElement(\"div\");\n  var script = document.createElement(\"script\");\n  node.appendChild(div);\n  node.appendChild(script);\n}\n\n/**\n * Handle when a new output is added\n */\nfunction handle_add_output(event, handle) {\n  var output_area = handle.output_area;\n  var output = handle.output;\n  if (!output.data.hasOwnProperty(EXEC_MIME_TYPE)) {\n    return\n  }\n  var id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n  var toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n  if (id !== undefined) {\n    var nchildren = toinsert.length;\n    toinsert[nchildren-1].children[0].innerHTML = output.data[HTML_MIME_TYPE];\n    toinsert[nchildren-1].children[1].textContent = output.data[JS_MIME_TYPE];\n    output_area._hv_plot_id = id;\n    if ((window.Bokeh !== undefined) && (id in Bokeh.index)) {\n      HoloViews.plot_index[id] = Bokeh.index[id];\n    } else {\n      HoloViews.plot_index[id] = null;\n    }\n  }\n}\n\n/**\n * Handle when an output is cleared or removed\n */\nfunction handle_clear_output(event, handle) {\n  var id = handle.cell.output_area._hv_plot_id;\n  if ((id === undefined) || !(id in HoloViews.plot_index)) { return; }\n  var comm = window.HoloViews.comm_manager.get_client_comm(\"hv-extension-comm\", \"hv-extension-comm\", function () {});\n  if (comm !== null) {\n    comm.send({event_type: 'delete', 'id': id});\n  }\n  delete HoloViews.plot_index[id];\n  if ((window.Bokeh !== undefined) & (id in window.Bokeh.index)) {\n    window.Bokeh.index[id].model.document.clear();\n    delete Bokeh.index[id];\n  }\n}\n\n/**\n * Handle kernel restart event\n */\nfunction handle_kernel_cleanup(event, handle) {\n  delete HoloViews.comms[\"hv-extension-comm\"];\n  window.HoloViews.plot_index = {}\n}\n\n/**\n * Handle update_display_data messages\n */\nfunction handle_update_output(event, handle) {\n  handle_clear_output(event, {cell: {output_area: handle.output_area}})\n  handle_add_output(event, handle)\n}\n\nfunction register_renderer(events, OutputArea) {\n  function append_mime(data, metadata, element) {\n    // create a DOM node to render to\n    var toinsert = this.create_output_subarea(\n    metadata,\n    CLASS_NAME,\n    EXEC_MIME_TYPE\n    );\n    this.keyboard_manager.register_events(toinsert);\n    // Render to node\n    var props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n    render(props, toinsert[0]);\n    element.append(toinsert);\n    return toinsert\n  }\n\n  events.on('output_added.OutputArea', handle_add_output);\t\n  events.on('output_updated.OutputArea', handle_update_output);\n  events.on('clear_output.CodeCell', handle_clear_output);\n  events.on('delete.Cell', handle_clear_output);\n  events.on('kernel_ready.Kernel', handle_kernel_cleanup);\n\n  OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n    safe: true,\n    index: 0\n  });\n}\n\nif (window.Jupyter !== undefined) {\n  try {\n    var events = require('base/js/events');\n    var OutputArea = require('notebook/js/outputarea').OutputArea;\n    if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n      register_renderer(events, OutputArea);\n    }\n  } catch(err) {\n  }\n}\n\n// Define MPL specific subclasses\nfunction MPLSelectionWidget() {\n  SelectionWidget.apply(this, arguments);\n}\n\nfunction MPLScrubberWidget() {\n  ScrubberWidget.apply(this, arguments);\n}\n\n// Let them inherit from the baseclasses\nMPLSelectionWidget.prototype = Object.create(SelectionWidget.prototype);\nMPLScrubberWidget.prototype = Object.create(ScrubberWidget.prototype);\n\n// Define methods to override on widgets\nvar MPLMethods = {\n  init_slider : function(init_val){\n    if(this.load_json) {\n      this.from_json()\n    } else {\n      this.update_cache();\n    }\n    if (this.dynamic | !this.cached | (this.current_vals === undefined)) {\n      this.update(0)\n    } else {\n      this.set_frame(this.current_vals[0], 0)\n    }\n  },\n  process_msg : function(msg) {\n    var data = msg.content.data;\n    this.frames[this.current] = data;\n    this.update_cache(true);\n    this.update(this.current);\n  }\n}\n// Extend MPL widgets with backend specific methods\nextend(MPLSelectionWidget.prototype, MPLMethods);\nextend(MPLScrubberWidget.prototype, MPLMethods);\n\nwindow.HoloViews.MPLSelectionWidget = MPLSelectionWidget\nwindow.HoloViews.MPLScrubberWidget = MPLScrubberWidget\n\n// Define Bokeh specific subclasses\nfunction BokehSelectionWidget() {\n  SelectionWidget.apply(this, arguments);\n}\n\nfunction BokehScrubberWidget() {\n  ScrubberWidget.apply(this, arguments);\n}\n\n// Let them inherit from the baseclasses\nBokehSelectionWidget.prototype = Object.create(SelectionWidget.prototype);\nBokehScrubberWidget.prototype = Object.create(ScrubberWidget.prototype);\n\n// Define methods to override on widgets\nvar BokehMethods = {\n  update_cache : function(){\n    for (var index in this.frames) {\n      this.frames[index] = JSON.parse(this.frames[index]);\n    }\n  },\n  update : function(current){\n    if (current === undefined) {\n      return;\n    }\n    var data = this.frames[current];\n    if (data !== undefined) {\n      if (data.root in HoloViews.plot_index) {\n        var doc = HoloViews.plot_index[data.root].model.document;\n      } else {\n        var doc = Bokeh.index[data.root].model.document;\n      }\n      doc.apply_json_patch(data.content);\n    }\n  },\n  init_comms: function() {\n    if (Bokeh.protocol !== undefined) {\n      this.receiver = new Bokeh.protocol.Receiver()\n    } else {\n      this.receiver = null;\n    }\n    return HoloViewsWidget.prototype.init_comms.call(this);\n  },\n  process_msg : function(msg) {\n    if (this.plot_id in HoloViews.plot_index) {\n      var doc = HoloViews.plot_index[this.plot_id].model.document;\n    } else {\n      var doc = Bokeh.index[this.plot_id].model.document;\n    }\n    if (this.receiver === null) { return }\n    var receiver = this.receiver;\n    if (msg.buffers.length > 0) {\n      receiver.consume(msg.buffers[0].buffer)\n    } else {\n      receiver.consume(msg.content.data)\n    }\n    const comm_msg = receiver.message;\n    if ((comm_msg != null) && (doc != null)) {\n      doc.apply_json_patch(comm_msg.content, comm_msg.buffers)\n    }\n  }\n}\n\n// Extend Bokeh widgets with backend specific methods\nextend(BokehSelectionWidget.prototype, BokehMethods);\nextend(BokehScrubberWidget.prototype, BokehMethods);\n\nwindow.HoloViews.BokehSelectionWidget = BokehSelectionWidget\nwindow.HoloViews.BokehScrubberWidget = BokehScrubberWidget\n\n    function JupyterCommManager() {\n    }\n\n    JupyterCommManager.prototype.register_target = function(plot_id, comm_id, msg_handler) {\n      if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n        var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n        comm_manager.register_target(comm_id, function(comm) {\n          comm.on_msg(msg_handler);\n        });\n      } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n        window.PyViz.kernels[plot_id].registerCommTarget(comm_id, function(comm) {\n          comm.onMsg = msg_handler;\n        });\n      }\n    }\n\n    JupyterCommManager.prototype.get_client_comm = function(plot_id, comm_id, msg_handler) {\n      if (comm_id in window.PyViz.comms) {\n        return window.PyViz.comms[comm_id];\n      } else if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n        var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n        var comm = comm_manager.new_comm(comm_id, {}, {}, {}, comm_id);\n        if (msg_handler) {\n          comm.on_msg(msg_handler);\n        }\n      } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n        var comm = window.PyViz.kernels[plot_id].connectToComm(comm_id);\n        comm.open();\n        if (msg_handler) {\n          comm.onMsg = msg_handler;\n        }\n      }\n\n      window.PyViz.comms[comm_id] = comm;\n      return comm;\n    }\n\n    window.PyViz.comm_manager = new JupyterCommManager();\n    "
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import holoviews as hv\n",
    "import geoviews as gv\n",
    "import cartopy\n",
    "\n",
    "from cartopy import crs\n",
    "from cartopy import feature as cf\n",
    "from geoviews import feature as gf\n",
    "\n",
    "hv.notebook_extension('matplotlib')\n",
    "%output backend='matplotlib'\n",
    "%opts Feature [projection=crs.Robinson()]\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%opts Feature.Lines (facecolor='none' edgecolor='gray')\n",
    "graticules = gv.Feature(cf.NaturalEarthFeature(category='physical', name='graticules_30',scale='110m'), group='Lines')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "After you hit the `Shift-Enter` button on this next cell, you should literally see a visual \"Hello World\"."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hello\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/opt/conda/lib/python3.6/site-packages/cartopy/io/__init__.py:260: DownloadWarning: Downloading: http://naciscdn.org/naturalearth/110m/physical/ne_110m_ocean.zip\n",
      "  warnings.warn('Downloading: {}'.format(url), DownloadWarning)\n",
      "/opt/conda/lib/python3.6/site-packages/cartopy/io/__init__.py:260: DownloadWarning: Downloading: http://naciscdn.org/naturalearth/110m/physical/ne_110m_land.zip\n",
      "  warnings.warn('Downloading: {}'.format(url), DownloadWarning)\n",
      "/opt/conda/lib/python3.6/site-packages/cartopy/io/__init__.py:260: DownloadWarning: Downloading: http://naciscdn.org/naturalearth/110m/physical/ne_110m_graticules_30.zip\n",
      "  warnings.warn('Downloading: {}'.format(url), DownloadWarning)\n",
      "/opt/conda/lib/python3.6/site-packages/cartopy/io/__init__.py:260: DownloadWarning: Downloading: http://naciscdn.org/naturalearth/110m/physical/ne_110m_rivers_lake_centerlines.zip\n",
      "  warnings.warn('Downloading: {}'.format(url), DownloadWarning)\n",
      "/opt/conda/lib/python3.6/site-packages/cartopy/io/__init__.py:260: DownloadWarning: Downloading: http://naciscdn.org/naturalearth/110m/physical/ne_110m_lakes.zip\n",
      "  warnings.warn('Downloading: {}'.format(url), DownloadWarning)\n",
      "/opt/conda/lib/python3.6/site-packages/cartopy/io/__init__.py:260: DownloadWarning: Downloading: http://naciscdn.org/naturalearth/110m/cultural/ne_110m_admin_0_boundary_lines_land.zip\n",
      "  warnings.warn('Downloading: {}'.format(url), DownloadWarning)\n",
      "/opt/conda/lib/python3.6/site-packages/cartopy/io/__init__.py:260: DownloadWarning: Downloading: http://naciscdn.org/naturalearth/110m/physical/ne_110m_coastline.zip\n",
      "  warnings.warn('Downloading: {}'.format(url), DownloadWarning)\n"
     ]
    },
    {
     "data": {
      "application/javascript": [
       "\n",
       "// Ugly hack - see #2574 for more information\n",
       "if (!(document.getElementById('139934965705192')) && !(document.getElementById('_anim_imgNone'))) {\n",
       "  console.log(\"Creating DOM nodes dynamically for assumed nbconvert export. To generate clean HTML output set HV_DOC_HTML as an environment variable.\")\n",
       "  var htmlObject = document.createElement('div');\n",
       "  htmlObject.innerHTML = `<div id='139934965705192' style='display: table; margin: 0 auto;'><img src='' style='max-width:100%; margin: auto; display: block; '/></div>`;\n",
       "  var scriptTags = document.getElementsByTagName('script');\n",
       "  var parentTag = scriptTags[scriptTags.length-1].parentNode;\n",
       "  parentTag.append(htmlObject)\n",
       "}\n"
      ],
      "application/vnd.holoviews_exec.v0+json": "",
      "text/html": [
       "<div id='139934965705192' style='display: table; margin: 0 auto;'><img src='' style='max-width:100%; margin: auto; display: block; '/></div>"
      ],
      "text/plain": [
       ":Overlay\n",
       "   .Ocean.I     :Feature   [Longitude,Latitude]\n",
       "   .Land.I      :Feature   [Longitude,Latitude]\n",
       "   .Lines.I     :Feature   [Longitude,Latitude]\n",
       "   .Rivers.I    :Feature   [Longitude,Latitude]\n",
       "   .Lakes.I     :Feature   [Longitude,Latitude]\n",
       "   .Borders.I   :Feature   [Longitude,Latitude]\n",
       "   .Coastline.I :Feature   [Longitude,Latitude]"
      ]
     },
     "execution_count": 4,
     "metadata": {
      "application/vnd.holoviews_exec.v0+json": {
       "id": 139934965705192
      }
     },
     "output_type": "execute_result"
    }
   ],
   "source": [
    "%%output size=450\n",
    "features = hv.Overlay([gf.ocean, gf.land, graticules, gf.rivers, gf.lakes, gf.borders, gf.coastline])\n",
    "print(\"Hello\")\n",
    "features"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Command Line from Notebook Cell\n",
    "\n",
    "You can type shell commands from notebook cells, too."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[0m\u001b[01;34marcgis\u001b[0m/    \u001b[01;34mfbprophet\u001b[0m/  HelloWorld.ipynb  \u001b[01;34mmdrtb-v5\u001b[0m/         \u001b[01;34msnow-cholera-v3\u001b[0m/\r\n",
      "\u001b[01;34mebola-v2\u001b[0m/  \u001b[01;34mfolium\u001b[0m/     \u001b[01;34mmdrtb-v4\u001b[0m/         \u001b[01;34msnow-cholera-v2\u001b[0m/  \u001b[01;34msnow-cholera-v4\u001b[0m/\r\n"
     ]
    }
   ],
   "source": [
    "ls"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "celltoolbar": "Create Assignment",
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.5"
  },
  "nbTranslate": {
   "displayLangs": [
    "*"
   ],
   "hotkey": "alt-t",
   "langInMainMenu": true,
   "sourceLang": "en",
   "targetLang": "fr",
   "useGoogleTranslate": true
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
